package com.robertbak.geolocation
{
	import com.robertbak.geolocation.events.GeoFaultEvent;
	import com.robertbak.geolocation.events.GeoResultEvent;
	import com.robertbak.geolocation.interfaces.IPosition;
	
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.IEventDispatcher;
	import flash.external.ExternalInterface;
	
	/**
	 * Dispatched when a new location has been successfully found, either because of a manual send() 
	 * or because the geolocator watching for location changes (following).   
	 */
	[Event(name="result", type="com.robertbak.geolocation.events.GeoResultEvent")]
	
	/**
	 * 	Dispatched when an attept to geolocate fails.
	 */
	[Event(name="fault", type="com.robertbak.geolocation.events.GeoFaultEvent")]	
	
	/**
	 * The main class.
	 * @author robert
	 */
	public class GeoLocator extends EventDispatcher
	{
		private var _initialized:Boolean = false;
		
		private var _available:Boolean = false;
		
		/**
		 * The name used while embedding the application in the HTML.
		 * This needs to be set!
		 **/
		public var applicationID:String;
		
		private var javascript:GeoJavascript; 
		/**
		 * Shows if the geolocation is available.
		 * @return 
		 */
		public function get available():Boolean
		{
			if (!_initialized) 	init();
			return _available;			
		}
		
		/**
		 * 
		 * @default 
		 */
		public var timeout:uint = 30000; 
		
		private var watchPositionID:uint = uint.MAX_VALUE; 
		
		private static const BINDING_RESULT:String = "resultForBinding";
		
		/**
		 * 
		 * @param target
		 */
		public function GeoLocator(target:IEventDispatcher=null)
		{
			if (ExternalInterface.available)
			{
				_available=true;
			}
			else 
			{
				_available = false;
			}
			super(target);
		}
		
		private function init():void
		{
			GeoJavascript.applicationID=applicationID;
			javascript=GeoJavascript.getInstance();
			if (javascript!=null)
			{				
				if (ExternalInterface.call('AsGeolocation.geoAvailible'))
				{
					_available=true;
					ExternalInterface.call('AsGeolocation.setGearsRequestAddress',_gearsRequestAddress);
					ExternalInterface.addCallback("geoResult", geoResult);
					ExternalInterface.addCallback("geoFault", geoFault);
				}
				else
				{
					_available=false;
					return;
				}
			}
			else
			{
				_available=false;
				return;
			}
		}
		
		private var _result:GeoPosition;
		
		[Bindable("resultForBinding")]
		
		/**
		 * Holds the last found location 
		 * @return 
		 */
		public function get lastResult():IPosition
		{
			return _result;
		}
		
		/**
		 *  Executes a geolocation request  
		 */
		public function send():void
		{
			if (available)
			{
				ExternalInterface.call('AsGeolocation.geoLocate',timeout);
			}
		}
		
		
		[Bindable("followingChange")]
		/**
		 * 
		 *  If true, tracking the user location.		
		 * @return 
		 */
		public function get following():Boolean
		{
			return !(watchPositionID==uint.MAX_VALUE);
		}
		
		/**
		 * 
		 * Enables the tracking of the user location.		 
		 * @return 
		 */
		public function follow():Boolean
		{
			if (available)
			{
				if (watchPositionID==uint.MAX_VALUE)
				{
					watchPositionID = ExternalInterface.call('AsGeolocation.geoLocateWatch');
					dispatchEvent(new Event('followingChange'));
				}
				return true;
			}
			return false;
		}
		
		/**
		 * Disables the tracking of the user location.
		 * @return 
		 */
		public function unfollow():Boolean
		{
			if (available)
			{
				if (watchPositionID!=uint.MAX_VALUE)
				{
					ExternalInterface.call('AsGeolocation.geoLocateUnWatch',watchPositionID);					
					watchPositionID = uint.MAX_VALUE;
					dispatchEvent(new Event('followingChange'));
				}
				return true;
			}
			return false;
		}
		
		private var _gearsRequestAddress:Boolean = false;
		
		/**
		 * 
		 * @param value
		 */
		public function set gearsRequestAddress (value:Boolean):void
		{
			_gearsRequestAddress = value;
			ExternalInterface.call('AsGeolocation.setGearsRequestAddress',_gearsRequestAddress);
		} 
		/**
		 * 
		 * @return 
		 */
		public function get gearsRequestAddress ():Boolean
		{
			return _gearsRequestAddress;
		} 
		private function geoResult(O:Object):void
		{			
			_result = new GeoPosition(O);
			dispatchEvent(new flash.events.Event(BINDING_RESULT));
			dispatchEvent(new GeoResultEvent("result",_result));
		}
		
		private function geoFault(O:Object):void
		{
			var fault:GeoPositionError = new GeoPositionError(O);
			dispatchEvent(new GeoFaultEvent("fault",fault));
		}			
	}
}
